package com.nulldev.util.concurrency.threadIt.v4.emapi;

import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.SynchronousQueue;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;

import com.nulldev.util.JVM.Arguments;
import com.nulldev.util.VariableAPI.MathUtil;
import com.nulldev.util.concurrency.threadIt.v3.ThreadDispatcher;
import com.nulldev.util.concurrency.threadIt.v3.executors.Lightning;
import com.nulldev.util.concurrency.threadIt.v3.executors.LimitThreadPool;
import com.nulldev.util.concurrency.threadIt.v3.extensions.Task;
import com.nulldev.util.concurrency.threadIt.v4.emapi.cft.CompleteableFutureTask;
import com.nulldev.util.concurrency.threadIt.v4.emapi.cntft.ControllableFutureTask;
import com.nulldev.util.concurrency.threadIt.v4.emapi.extensions.ExecutorFeatureExtensions;
import com.nulldev.util.concurrency.threadIt.v4.emapi.extensions.ExtendedExecutor;
import com.nulldev.util.concurrency.threadIt.v4.emapi.extensions.IFutureTaskFlags;
import com.nulldev.util.concurrency.threadIt.v4.emapi.ft.FutureTask;
import com.nulldev.util.concurrency.threadIt.v4.emapi.ft2.FutureTask2;
import com.nulldev.util.concurrency.threadIt.v4.emapi.uft.Executable;
import com.nulldev.util.concurrency.threadIt.v4.emapi.uft.Returnable;
import com.nulldev.util.concurrency.threadIt.v4.executors.DATE;
import com.nulldev.util.concurrency.threadIt.v4.executors.LightningFJ;
import com.nulldev.util.concurrency.threadIt.v4.executors.PBTE;
import com.nulldev.util.concurrency.threadIt.v4.executors.bqde.BQDE;
import com.nulldev.util.concurrency.threadIt.v4.executors.cfs.CompletelyFairExecutorService;
import com.nulldev.util.concurrency.threadIt.v4.executors.dcte.DCTE;
import com.nulldev.util.concurrency.threadIt.v4.executors.etpe.EnhancedThreadPoolExecutor;
import com.nulldev.util.concurrency.threadIt.v4.executors.fje.ForkJoinExecutorService;
import com.nulldev.util.concurrency.threadIt.v4.sched.ASyncScheduler;
import com.nulldev.util.concurrency.threadIt.v4.sched.impl.CAS.ContextuallyAwareAsyncScheduler;
import com.nulldev.util.concurrency.threadIt.v4.sched.impl.CAS.ContextuallyAwareAsyncScheduler2;
import com.nulldev.util.concurrency.threadIt.v4.sched.impl.bq.BoundedQueueASyncScheduler;
import com.nulldev.util.concurrency.threadIt.v4.sched.impl.fq.FastQueueASyncScheduler;
import com.nulldev.util.concurrency.threadIt.v4.sched.impl.standard.StandardQueuedASyncScheduler;
import com.nulldev.util.concurrency.threadIt.v4.utils.impls.TEConditions;
import com.nulldev.util.data.Variables;
import com.nulldev.util.logging.Logger;
import com.nulldev.util.logging.LoggerFactory;
import com.nulldev.util.manage.Version;
import com.nulldev.util.manage.nullUtil;

@SuppressWarnings("deprecation")
public class ExecutorManager extends Thread implements AutoCloseable, IExecutorManager, Runnable {

	/**
	 * (SOMEHOW) STABLE AND WORKING! <br>
	 * <br>
	 * Complete:<br>
	 * - make() -> Executable to allow for seamless API switching via Executable<T>
	 * <br>
	 * - Remove getActiveThreads() as tracking is no longer required. <br>
	 * - Add Arguments API selection <br>
	 * - Move to getService() <br>
	 * - Allow for runtime-executor switching. <br>
	 * - Add async submissions
	 */

	public static final Version LIB_VERSION = new Version("4.0.4");

	private ExecutorService executor;
	private final AtomicBoolean nativeBatchingPresent = new AtomicBoolean(),
			/* */
			batchingWorkaroundInUse = new AtomicBoolean(),
			/* */
			levelledBatchingWorkaroundInUse = new AtomicBoolean(),
			/* */
			drainTasksSupported = new AtomicBoolean(),
			/* */
			schedQueueBypassSupported = new AtomicBoolean(),
			/* */
			supportsExecutionPreferences = new AtomicBoolean(),
			/* */
			supportsLowPriorityThreads = new AtomicBoolean(),
			/* */
			supportsAttemptableSubmissions2 = new AtomicBoolean();
	private final boolean allowExecutorSwitching;

	private static final int DEFAULT_LOW_CORE_COUNT = 20;

	private static int getDefaultThreadCount() {
		final int x0 = Runtime.getRuntime().availableProcessors();
		if (x0 <= 2)
			return DEFAULT_LOW_CORE_COUNT;
		return Math.min(32, x0 * Math.max(256, x0 * 6));
	}

	public static ASyncScheduler getScheduler(final IExecutorManager executor) {
		Variables.requireNonNull(executor, "executor");
		if (Arguments.hasArgument("--EMAPI.useBQ"))
			return new BoundedQueueASyncScheduler(executor);
		if (Arguments.hasArgument("--EMAPI.useSTD"))
			return new StandardQueuedASyncScheduler(executor);
		if (Arguments.hasArgument("--EMAPI.useFQ"))
			return new FastQueueASyncScheduler(executor);
		if (Arguments.hasArgument("--EMAPI.useCAAS1"))
			return new ContextuallyAwareAsyncScheduler(executor);
		return new ContextuallyAwareAsyncScheduler2(executor);
	}

	public static ExecutorService getService() {
		if (Arguments.hasArgument("--EMAPI.useTD", "--threadit3.usePBTE4", "--threadit3.useLightningFJ", "--threadit3.useLBTE4", "--threadit3.useDATE4",
				"--threadit3.useDATE4_constricted") && !nullUtil.IS_ANDROID_BUILD) {
			log.warn("You are using a threadIt! v3.x executor, this feature will no longer be supported in 4.1.0.");
			return ThreadDispatcher.instance().getExecutor();
		} else if (Arguments.hasArgument("--EMAPI.useLightningFJ")) {
			return new LightningFJ(getDefaultThreadCount());
		} else if (Arguments.hasArgument("--EMAPI.useDATE")) {
			return new DATE(getDefaultThreadCount());
		} else if (Arguments.hasArgument("--EMAPI.useDCTE")) {
			return new DCTE(getDefaultThreadCount());
		} else if (Arguments.hasArgument("--EMAPI.usePBTE")) {
			return new PBTE(getDefaultThreadCount());
		} else if (Arguments.hasArgument("--EMAPI.useETPE")) {
			return new EnhancedThreadPoolExecutor(Runtime.getRuntime().availableProcessors(), getDefaultThreadCount(), 60L, TimeUnit.SECONDS, true,
					new SynchronousQueue<Runnable>(true), new EnhancedThreadPoolExecutor.RequeuePolicy());
		} else if (Arguments.hasArgument("--EMAPI.useCFS")) {
			final int min = MathUtil.clamp(8, 256, Runtime.getRuntime().availableProcessors());
			final int cpus = Math.max(8, getDefaultThreadCount());
			final int leases = MathUtil.clamp(8, 64, min);
			return CompletelyFairExecutorService.builder().withCorePoolSize(min).withMaxPoolSize(cpus).withLeasesPerThread(leases).withMaxLeasesPerThread(cpus)
					.withThreadNameFormat("CFS-%s").withKeepAliveTime(60L, TimeUnit.SECONDS).build();
		} else if (Arguments.hasArgument("--EMAPI.useFJE")) {
			return new ForkJoinExecutorService("threadIt4-FJE-", getDefaultThreadCount(), true);
		} else if (Arguments.hasArgument("--EMAPI.useLegacyLTP")) {
			log.warn("You are using a threadIt! v3.x executor, this feature will no longer be supported in 4.1.0.");
			return new LimitThreadPool(getDefaultThreadCount());
		} else if (Arguments.hasArgument("--EMAPI.useLegacyLightning")) {
			log.warn("You are using a threadIt! v3.x executor, this feature will no longer be supported in 4.1.0.");
			return new Lightning(getDefaultThreadCount());
		} else if (Arguments.hasArgument("--EMAPI.useBQDE", "--EMAPI.IWantABrokenExecutor")) {
			return new BQDE(getDefaultThreadCount(), ENABLE_BATCH_OPERATIONS ? BQDE.getOptimalBatchingSize() : 0, true);
		} else {
			return new BQDE(getDefaultThreadCount(), ENABLE_BATCH_OPERATIONS ? BQDE.getOptimalBatchingSize() : 0, true);
		}
	}

	public ExecutorManager() {
		this(false);
	}

	public ExecutorManager(final boolean allowExecutorSwitching) {
		super("ExecutorManager: Dispatch/ASYNC Thread");
		this.executor = getService();
		this.ASYNC_SCHED = getScheduler(this);
		this.allowExecutorSwitching = allowExecutorSwitching;
		this.nativeBatchingPresent
				.set(this.executor instanceof ExtendedExecutor && ((ExtendedExecutor) this.executor).executorSupportsBatching() && ENABLE_BATCH_OPERATIONS);
		this.batchingWorkaroundInUse.set(this.executor instanceof ExtendedExecutor
				&& ((ExtendedExecutor) this.executor).supportsFeature(ExecutorFeatureExtensions.EEX_workaround_force_native_batching)
				&& ENABLE_BATCH_OPERATIONS);
		this.levelledBatchingWorkaroundInUse.set(this.executor instanceof ExtendedExecutor
				&& (((ExtendedExecutor) this.executor).supportsFeature(ExecutorFeatureExtensions.EEX_workaround_force_disable_l1_batching)
						|| ((ExtendedExecutor) this.executor).supportsFeature(ExecutorFeatureExtensions.EEX_workaround_force_disable_l2_batching))
				&& ENABLE_BATCH_OPERATIONS);
		this.drainTasksSupported.set(this.executor instanceof ExtendedExecutor
				&& ((ExtendedExecutor) this.executor).supportsFeature(ExecutorFeatureExtensions.EEX_core_drain_task_queue) && ENABLE_BATCH_OPERATIONS);
		this.supportsExecutionPreferences.set(this.executor instanceof ExtendedExecutor
				&& ((ExtendedExecutor) this.executor).supportsFeature(ExecutorFeatureExtensions.EEX_core_execute_with_preference));
		this.supportsLowPriorityThreads.set(this.executor instanceof ExtendedExecutor
				&& ((ExtendedExecutor) this.executor).supportsFeature(ExecutorFeatureExtensions.EEX_executor_low_priority_threads)
				&& ((ExtendedExecutor) this.executor).supportsLPT() && ((ExtendedExecutor) this.executor).isLPTActive());
		this.schedQueueBypassSupported.set(this.executor instanceof ExtendedExecutor
				&& ((ExtendedExecutor) this.executor).supportsFeature(ExecutorFeatureExtensions.EEX_executor_sched_accepts_always));
		this.supportsAttemptableSubmissions2.set(this.executor instanceof ExtendedExecutor
				&& ((ExtendedExecutor) this.executor).supportsFeature(ExecutorFeatureExtensions.EEX_executor_attemptable_submissions2));
		super.start();
	}

	public ExecutorManager(final ExecutorService executor) {
		this(executor, false);
	}

	public ExecutorManager(final ExecutorService executor, final boolean allowExecutorSwitching) {
		super("ExecutorManager: Dispatch/ASYNC Thread");
		if (executor == null)
			throw new NullPointerException("executor == null!");
		if (executor.isShutdown() || executor.isTerminated())
			throw new UnsupportedOperationException("Cannot use a dead executor!");
		this.executor = executor;
		this.ASYNC_SCHED = getScheduler(this);
		this.allowExecutorSwitching = allowExecutorSwitching;
		this.nativeBatchingPresent
				.set(this.executor instanceof ExtendedExecutor && ((ExtendedExecutor) this.executor).executorSupportsBatching() && ENABLE_BATCH_OPERATIONS);
		this.batchingWorkaroundInUse.set(this.executor instanceof ExtendedExecutor
				&& ((ExtendedExecutor) this.executor).supportsFeature(ExecutorFeatureExtensions.EEX_workaround_force_native_batching)
				&& ENABLE_BATCH_OPERATIONS);
		this.levelledBatchingWorkaroundInUse.set(this.executor instanceof ExtendedExecutor
				&& (((ExtendedExecutor) this.executor).supportsFeature(ExecutorFeatureExtensions.EEX_workaround_force_disable_l1_batching)
						|| ((ExtendedExecutor) this.executor).supportsFeature(ExecutorFeatureExtensions.EEX_workaround_force_disable_l2_batching))
				&& ENABLE_BATCH_OPERATIONS);
		this.drainTasksSupported.set(this.executor instanceof ExtendedExecutor
				&& ((ExtendedExecutor) this.executor).supportsFeature(ExecutorFeatureExtensions.EEX_core_drain_task_queue) && ENABLE_BATCH_OPERATIONS);
		this.supportsExecutionPreferences.set(this.executor instanceof ExtendedExecutor
				&& ((ExtendedExecutor) this.executor).supportsFeature(ExecutorFeatureExtensions.EEX_core_execute_with_preference));
		this.supportsLowPriorityThreads.set(this.executor instanceof ExtendedExecutor
				&& ((ExtendedExecutor) this.executor).supportsFeature(ExecutorFeatureExtensions.EEX_executor_low_priority_threads)
				&& ((ExtendedExecutor) this.executor).supportsLPT() && ((ExtendedExecutor) this.executor).isLPTActive());
		this.schedQueueBypassSupported.set(this.executor instanceof ExtendedExecutor
				&& ((ExtendedExecutor) this.executor).supportsFeature(ExecutorFeatureExtensions.EEX_executor_sched_accepts_always));
		this.supportsAttemptableSubmissions2.set(this.executor instanceof ExtendedExecutor
				&& ((ExtendedExecutor) this.executor).supportsFeature(ExecutorFeatureExtensions.EEX_executor_attemptable_submissions));
		super.start();
	}

	/*
	 * WARNING: UNSAFE
	 */
	@Override
	public void setExecutor(final ExecutorService service) {
		if (!this.allowExecutorSwitching)
			throw new UnsupportedOperationException("Executor switching is disallowed.");
		if (service == null || service.isShutdown() || service.isTerminated())
			throw new UnsupportedOperationException("Illegal service given!");
		final List<Runnable> paused = this.executor.shutdownNow();
		this.executor = service;
		this.nativeBatchingPresent
				.set(service instanceof ExtendedExecutor && ((ExtendedExecutor) service).executorSupportsBatching() && ENABLE_BATCH_OPERATIONS);
		this.batchingWorkaroundInUse.set(service instanceof ExtendedExecutor
				&& ((ExtendedExecutor) service).supportsFeature(ExecutorFeatureExtensions.EEX_workaround_force_native_batching) && ENABLE_BATCH_OPERATIONS);
		this.levelledBatchingWorkaroundInUse.set(service instanceof ExtendedExecutor
				&& (((ExtendedExecutor) service).supportsFeature(ExecutorFeatureExtensions.EEX_workaround_force_disable_l1_batching)
						|| ((ExtendedExecutor) service).supportsFeature(ExecutorFeatureExtensions.EEX_workaround_force_disable_l2_batching))
				&& ENABLE_BATCH_OPERATIONS);
		this.drainTasksSupported.set(service instanceof ExtendedExecutor
				&& ((ExtendedExecutor) service).supportsFeature(ExecutorFeatureExtensions.EEX_core_drain_task_queue) && ENABLE_BATCH_OPERATIONS);
		this.supportsExecutionPreferences.set(service instanceof ExtendedExecutor
				&& ((ExtendedExecutor) service).supportsFeature(ExecutorFeatureExtensions.EEX_core_execute_with_preference));
		this.supportsLowPriorityThreads.set(
				service instanceof ExtendedExecutor && ((ExtendedExecutor) service).supportsFeature(ExecutorFeatureExtensions.EEX_executor_low_priority_threads)
						&& ((ExtendedExecutor) service).supportsLPT() && ((ExtendedExecutor) service).isLPTActive());
		this.schedQueueBypassSupported.set(service instanceof ExtendedExecutor
				&& ((ExtendedExecutor) service).supportsFeature(ExecutorFeatureExtensions.EEX_executor_sched_accepts_always));
		this.supportsAttemptableSubmissions2.set(service instanceof ExtendedExecutor
				&& ((ExtendedExecutor) service).supportsFeature(ExecutorFeatureExtensions.EEX_executor_attemptable_submissions));
		this.ASYNC_SCHED.executorUpdated(service);
		paused.forEach((r) -> {
			service.execute(r);
		});
	}

	@Override
	public boolean isRunning() {
		return !this.STOP.get();
	}

	@Override
	public void close() throws Exception {
		this.STOP.set(true);
		this.executor.shutdownNow().clear();
		this.ASYNC_SCHED.clearQueues();
	}

	@Override
	public <T> Task<T> queueTask(final Task<T> t) {
		TEConditions.validExecutable(t);
		this.executor.execute(() -> {
			t.run_st();
		});
		return t;
	}

	@Override
	public void queueTask(final Runnable r, final ExecutionPreference e) {
		TEConditions.validRunnable(r);
		TEConditions.validPreference(e);
		if (this.supportsExecutionPreferences.get()) {
			((ExtendedExecutor) this.executor).execute(r, e);
		} else {
			this.executor.execute(r);
		}
	}

	private static final Logger log = LoggerFactory.getLogger();

	@Override
	public <T> FutureTask<T> queueTask(final FutureTask<T> t) {
		TEConditions.validExecutable(t);
		TEConditions.warnIfRunning(t);

		t.signalQueued();
		this.executor.execute(t);
		return t;
	}

	public <T> FutureTask2<T> queueTask(final FutureTask2<T> t) {
		TEConditions.validExecutable(t);
		TEConditions.warnIfRunning(t);

		t.signalQueued();
		this.executor.execute(t);
		return t;
	}

	@Override
	public <T> FutureTask<T> queueTaskLP(final FutureTask<T> t) {
		TEConditions.validExecutable(t);

		if (!this.supportsLowPriorityThreads.get())
			return queueTask(t);

		TEConditions.warnIfRunning(t);

		t.signalQueued();
		((ExtendedExecutor) this.executor).submitLP((Runnable) t);
		return t;
	}

	public <T> FutureTask2<T> queueTaskLP(final FutureTask2<T> t) {
		TEConditions.validExecutable(t);

		if (!this.supportsLowPriorityThreads.get())
			return queueTask(t);

		TEConditions.warnIfRunning(t);

		t.signalQueued();
		((ExtendedExecutor) this.executor).submitLP((Runnable) t);
		return t;
	}

	public <T> CompleteableFutureTask<T> queueTask(final CompleteableFutureTask<T> t) {
		TEConditions.validExecutable(t);

		t.signalQueued();
		this.executor.execute(t);
		return t;
	}

	public <T> CompleteableFutureTask<T> queueTaskLP(final CompleteableFutureTask<T> t) {
		TEConditions.validExecutable(t);
		if (!this.supportsLowPriorityThreads.get())
			return queueTask(t);

		TEConditions.warnIfRunning(t);

		t.signalQueued();
		((ExtendedExecutor) this.executor).submitLP((Runnable) t);
		return t;
	}

	@Override
	public <T> Executable<T> queueTask(final Executable<T> task) {
		TEConditions.validExecutable(task);

		if (task instanceof Task<?>) {
			this.queueTask((Task<?>) task);
			return task;
		} else if (task instanceof CompleteableFutureTask<?>) {
			this.queueTask((CompleteableFutureTask<?>) task);
			return task;
		} else if (task instanceof ControllableFutureTask<?>) {
			return task;
		} else if (task instanceof FutureTask2<?>) {
			this.queueTask((FutureTask2<?>) task);
			return task;
		} else if (task instanceof FutureTask<?>) {
			this.queueTask((FutureTask<?>) task);
			return task;
		} else {
			throw new UnsupportedOperationException("Cannot execute: " + task);
		}
	}

	@Override
	public <T> Executable<T> queueTaskLP(final Executable<T> task) {
		TEConditions.validExecutable(task);
		if (task instanceof Task<?>) {
			this.queueTask((Task<?>) task);
			return task;
		} else if (task instanceof CompleteableFutureTask<?>) {
			this.queueTaskLP((CompleteableFutureTask<?>) task);
			return task;
		} else if (task instanceof ControllableFutureTask<?>) {
			return task;
		} else if (task instanceof FutureTask<?>) {
			this.queueTaskLP((FutureTask<?>) task);
			return task;
		} else if (task instanceof FutureTask2<?>) {
			this.queueTaskLP((FutureTask2<?>) task);
			return task;
		} else {
			throw new UnsupportedOperationException("Cannot execute: " + task);
		}
	}

	@Override
	public void awaitIdle() {
		if (this.executor instanceof Lightning) {
			((Lightning) this.executor).awaitIdle();
		} else if (this.executor instanceof LightningFJ) {
			((LightningFJ) this.executor).awaitIdle();
		} else if (this.executor instanceof DATE) {
			((DATE) this.executor).awaitIdle();
		} else if (this.executor instanceof PBTE) {
			((PBTE) this.executor).awaitIdle();
		} else if (this.executor instanceof LimitThreadPool) {
			((LimitThreadPool) this.executor).awaitIdle();
		} else if (this.executor instanceof ExtendedExecutor
				&& ((ExtendedExecutor) this.executor).supportsFeature(ExecutorFeatureExtensions.EEX_executor_await_idle)) {
			((ExtendedExecutor) this.executor).awaitIdle();
		} else {
			throw new UnsupportedOperationException("Cannot wait for idle on executor: " + this.executor);
		}
	}

	@Override
	public void queueTask(final Runnable t) {
		TEConditions.validRunnable(t);
		this.executor.execute(t);
	}

	@Override
	public <T> Executable<T> tryToAsyncSubmit(final Returnable<T> r) {
		TEConditions.validReturnable(r);
		if (this.supportsAttemptableSubmissions2.get()) {
			final Executable<T> task = make(r, IFutureTaskFlags.FT_ATTEMPTABLE_TASK);

			/* The executor is in charge of signaling */
			((ExtendedExecutor) this.executor).attempt_submit2(task);

			return task;
		} else {
			return this.asyncSubmit(r);
		}
	}

	@Override
	public <T> Executable<T> tryToSubmit(final Returnable<T> r) {
		TEConditions.validReturnable(r);
		if (this.supportsAttemptableSubmissions2.get()) {

			final Executable<T> task = make(r);
			/* The executor is in charge of signaling */
			((ExtendedExecutor) this.executor).attempt_submit2(task);

			return task;
		} else {
			return submit(r);
		}
	}

	public static final boolean ENABLE_NEW_AWAIT_METHOD = Arguments.notArguments("--ExecutorManager.disableNewAwaitLogic");
	public static final boolean USE_LAZY_AWAIT = Arguments.notArguments("--ExecutorManager.disableLazyAwait");
	public static final boolean USE_COMPLETEABLE_FUTURE = Arguments.hasArgument("--ExecutorManager.useCompletableFT");

	public static <T> Executable<T> make(final Returnable<T> r) {
		return make(r, 0);
	}

	public static <T> Executable<T> make(final Returnable<T> r, final int FLAGS) {
		TEConditions.validReturnable(r);
		if (USE_COMPLETEABLE_FUTURE) {
			return new CompleteableFutureTask<T>() {

				@Override
				public T execute() {
					return r.run();
				}
			};
		} else if (USE_STAGING_FT) {
			return new FutureTask2<T>() {

				@Override
				public T execute() {
					return r.run();
				}

				@Override
				public Object getFlag(final int flag) {
					if ((FLAGS & flag) == flag)
						return FT_TRUE;
					return super.getFlag(flag);
				}

				@Override
				public int getFlags() {
					return FLAGS | super.getFlags();
				}
			};
		} else {
			return new FutureTask<T>() {

				@Override
				public T execute() {
					return r.run();
				}

				@Override
				public Object getFlag(final int flag) {
					if ((FLAGS & flag) == flag)
						return FT_TRUE;
					return super.getFlag(flag);
				}

				@Override
				public int getFlags() {
					return FLAGS | super.getFlags();
				}
			};
		}
	}

	public static <T> Executable<T> makeTaskNoAccel(final Returnable<T> r) {
		return makeTaskNoAccel(r, 0);
	}

	public static <T> Executable<T> makeTaskNoAccel(final Returnable<T> r, final int FLAGS) {
		TEConditions.validReturnable(r);
		if (USE_STAGING_FT) {
			return new FutureTask2<T>() {

				@Override
				public T execute() {
					return r.run();
				}

				@Override
				public Object getFlag(final int flag) {
					if ((FLAGS & flag) == flag)
						return FT_TRUE;
					return super.getFlag(flag);
				}

				@Override
				public int getFlags() {
					return FLAGS | super.getFlags();
				}
			};
		} else {
			return new FutureTask<T>() {

				@Override
				public T execute() {
					return r.run();
				}

				@Override
				public Object getFlag(final int flag) {
					if ((FLAGS & flag) == flag)
						return FT_TRUE;
					return super.getFlag(flag);
				}

				@Override
				public int getFlags() {
					return FLAGS | super.getFlags();
				}
			};
		}
	}

	private static IExecutorManager global_instance;
	static {
		if (global_instance == null) {
			global_instance = new ExecutorManager(true) {
				@Override
				public void close() throws Exception {
					throw new UnsupportedOperationException("Cannot shutdown the global executor manager!");
				}
			};
		}
	}

	public static <T> Executable<T> globalSubmit(final Returnable<T> r) {
		TEConditions.validReturnable(r);
		return global_instance.queueTask(make(r));
	}

	public static <T> Executable<T> globalSubmitLP(final Returnable<T> r) {
		TEConditions.validReturnable(r);
		return global_instance.queueTaskLP(make(r, IFutureTaskFlags.FT_LOW_PRIORITY));
	}

	public static <T> Executable<T> globalAsyncSubmit(final Returnable<T> r) {
		TEConditions.validReturnable(r);
		return global_instance.asyncSubmit(make(r, IFutureTaskFlags.FT_PREFER_BATCHING));
	}

	public static <T> Executable<T> globalAsyncSubmit(final Returnable<T> r, final int flags) {
		TEConditions.validReturnable(r);
		return global_instance.asyncSubmit(make(r, flags <= 0 ? IFutureTaskFlags.FT_PREFER_BATCHING : flags));
	}

	public static <T> Executable<T> globalSubmitNoAccel(final Returnable<T> r) {
		TEConditions.validReturnable(r);
		return global_instance.queueTask(makeTaskNoAccel(r));
	}

	public static <T> Executable<T> globalSubmitLPNoAccel(final Returnable<T> r) {
		TEConditions.validReturnable(r);
		return global_instance.queueTaskLP(makeTaskNoAccel(r));
	}

	public static IExecutorManager get() {
		return global_instance;
	}

	@Override
	public ExecutorService executor() {
		return this.executor;
	}

	@Override
	public ASyncScheduler scheduler() {
		return this.ASYNC_SCHED;
	}

	public static final String[] EXECUTOR_SERVICE_ARGS = new String[]
		{ "--EMAPI.useTD", "--threadit3.usePBTE4", "--threadit3.useLightningFJ", "--threadit3.useDATE4", "--threadit3.useDATE4_constricted",
				"--EMAPI.IWantABrokenExecutor", "--EMAPI.useDATE", "--EMAPI.usePBTE", "--EMAPI.useLBTE", "--EMAPI.useLegacyLTP", "--EMAPI.useLegacyLightning",
				"--EMAPI.useBQDE", "--EMAPI.useETPE", "--EMAPI.useFJE", "--EMAPI.useDCTE", "--EMAPI.useCFS" };

	public static final String[] ASYNC_SCHEDULER_ARGS = new String[]
		{ "--EMAPI.useFQ", "--EMAPI.useBQ", "--EMAPI.useSTD" };

	public static boolean hasCustomServices() {
		return Arguments.hasArgument(EXECUTOR_SERVICE_ARGS);
	}

	private final AtomicBoolean STOP = new AtomicBoolean();
	private final ASyncScheduler ASYNC_SCHED;

	public static final boolean ENABLE_BATCH_OPERATIONS = true;
	public static final boolean ENABLE_DEBUGGING = Arguments.hasArgument("--EMAPI.debug");
	public static final boolean ENABLE_TRACING = Arguments.hasArgument("--EMAPI.trace");

	@Override
	public void asyncSubmit(final Runnable r) {
		TEConditions.validRunnable(r);
		if (ENABLE_BATCH_OPERATIONS) {
			this.ASYNC_SCHED.submit_async(r);
		} else {
			this.executor.execute(r);
		}
	}

	@Override
	public <T> Executable<T> asyncSubmit(final Executable<T> r) {
		TEConditions.validExecutable(r);
		if (ENABLE_BATCH_OPERATIONS)
			return this.ASYNC_SCHED.submit_async(r);
		else {
			if (r instanceof Runnable) {
				try {
					this.executor.execute((Runnable) r);
					r.signalQueued();
					return r;
				} catch (Throwable e) {
					throw new RuntimeException(e);
				}
			} else {
				try {
					this.executor.execute(() -> {
						r.run_st();
					});
					r.signalQueued();
					return r;
				} catch (Throwable e) {
					throw new RuntimeException(e);
				}
			}
		}
	}

	@Override
	public void asyncSubmitAll(final Runnable... r) {
		if (r == null || r.length == 0)
			return;
		this.ASYNC_SCHED.submit_all_async(r);
	}

	@Override
	public <T> Executable<T> autoAsyncSubmit(final boolean enable, final Returnable<T> r) {
		TEConditions.validReturnable(r);
		if (this.nativeBatchingPresent.get() && enable) {
			final Executable<T> f = makeTaskNoAccel(r, IFutureTaskFlags.FT_PREFER_BATCHING);
			((ExtendedExecutor) this.executor).batch((Runnable) f);
			f.asyncSignalQueued();
			return f;
		} else if (enable) {
			return this.asyncSubmit(r);
		} else
			return this.submit(r);
	}

	@Override
	public <T> Executable<T> asyncSubmit(final Returnable<T> r) {
		TEConditions.validReturnable(r);
		if (ENABLE_BATCH_OPERATIONS)
			return this.ASYNC_SCHED.submit_async(r);
		else {
			final Executable<T> o = makeTaskNoAccel(r, IFutureTaskFlags.FT_PREFER_BATCHING);
			this.queueTask(o);
			o.asyncSignalQueued();
			return o;
		}
	}

	@Override
	public boolean supportsNativeBatching() {
		return this.nativeBatchingPresent.get();
	}

	@Override
	public boolean isWorkingAroundBatching() {
		return this.batchingWorkaroundInUse.get();
	}

	@Override
	public boolean isWorkingAroundLevelledBatching() {
		return this.levelledBatchingWorkaroundInUse.get();
	}

	@Override
	public boolean supportsTaskDrain() {
		return this.drainTasksSupported.get();
	}

	@Override
	public boolean supportsSchedQueueBypass() {
		return this.schedQueueBypassSupported.get();
	}

	@Override
	public <T> Executable<T> asyncSubmit_NoAccel(final Returnable<T> r) {
		TEConditions.validReturnable(r);
		try {
			final Executable<T> f = makeTaskNoAccel(r, IFutureTaskFlags.FT_PREFER_BATCHING);
			if (this.nativeBatchingPresent.get()) {
				((ExtendedExecutor) this.executor).batch((Runnable) f);
				f.signalQueued();
				return f;
			} else {
				this.ASYNC_SCHED.submit_async(f);
				f.signalQueued();
				return f;
			}
		} catch (Exception e) {
			throw new RuntimeException(e);
		}
	}

	@Override
	public void run() {
		this.ASYNC_SCHED.run();
	}

}
